package main

import (
	"fmt"
	"reflect"
)

func main() {
	// 声明一个空结构体
	type cat struct {
		Name string
		// 带有结构体tag的字段
		Type int `json:"type" id:"100"`
	}
	// 创建cat的实例
	ins := cat{Name: "mimi", Type: 1}
	// 获取结构体实例的反射类型对象
	typeOfCat := reflect.TypeOf(ins)
	// 遍历结构体所有成员
	for i := 0; i < typeOfCat.NumField(); i++ {
		// 获取每个成员的结构体字段类型
		fieldType := typeOfCat.Field(i)
		// 输出成员名和tag
		fmt.Printf("name: %v  tag: '%v'\n", fieldType.Name, fieldType.Tag)
	}
	// 通过字段名, 找到字段类型信息
	if catType, ok := typeOfCat.FieldByName("Type"); ok {
		// 从tag中取出需要的tag
		fmt.Println(catType.Name, "'s json key, value is", catType.Tag.Get("json"))
		fmt.Println(catType.Name, "'s id key, value is", catType.Tag.Get("id"))
	}
}

// 结构体标签（Struct Tag）
// 通过 reflect.Type 获取结构体成员信息 reflect.StructField 结构中的 Tag 被称为结构体标签（StructTag）。结构体标签是对结构体字段的额外信息标签
// 1) 结构体标签的格式
// Tag 在结构体字段后方书写的格式如下：
// key1:"value1" key2:"value2"
// 2) 从结构体标签中获取值
// StructTag 拥有一些方法，可以进行 Tag 信息的解析和提取，如下所示：
// func (tag StructTag) Get(key string) string：根据 Tag 中的键获取对应的值，例如`key1:"value1" key2:"value2"`的 Tag 中，可以传入“key1”获得“value1”。
// func (tag StructTag) Lookup(key string) (value string, ok bool)：根据 Tag 中的键，查询值是否存在。
// 3) 结构体标签格式错误导致的问题
// 编写 Tag 时，必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差，一旦格式写错，编译和运行时都不会提示任何错误，

// Lookup 是 Go 标准库给结构体字段 tag 提供的“安全查询”方法
// | 代码            | key 存在         | key 不存在          |
// | ------------- | -------------- | ---------------- |
// | `Get(key)`    | 返回对应值          | 返回 `""`          |
// | `Lookup(key)` | 返回 `(值, true)` | 返回 `("", false)` |
